package com.thx.common.orm;

import com.thx.common.util.ReflectionUtils;
import com.thx.common.util.ThreadLocalContext;
import com.thx.common.web.Page;
import com.thx.common.web.PropertyFilter;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.transform.Transformers;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.List;

/**
 * 基于 HibernateDao 基础上提供翻页功能.
 * 
 * @author balance
 */
@Repository
public class PaginationDao<T> extends HibernateBaseDao<T> {

  /**
   * 按属性过滤条件列表分页查找对象.
   */
  public Page<T> findPage(final Page<T> page, final List<PropertyFilter> filters) {
    Criterion[] criterions = buildCriterionByPropertyFilter(filters);
    return findPage(page, criterions);
  }

  /**
   * 按Criteria分页查询.
   * 
   * @param page 分页参数.
   * @param criterions 数量可变的Criterion.
   * 
   * @return 分页查询结果.附带结果列表及所有查询输入参数.
   */
  @SuppressWarnings("unchecked")
  public Page<T> findPage(final Page<T> page, final Criterion... criterions) {
    Assert.notNull(page, "page不能为空");

    Criteria criteria = createCriteria(criterions);

    if (page.isAutoCount()) {
      long totalCount = countCriteriaResult(criteria);
      page.setCount(new Long(totalCount).intValue());
    }

    setPageParameterToCriteria(criteria, page);

    List result = criteria.list();
    page.setResult(result);
    return page;
  }

  /**
   * 设置分页参数到Criteria对象,辅助函数.
   */
  protected Criteria setPageParameterToCriteria(final Criteria criteria, final Page<T> page) {

    Assert.isTrue(page.getPerpage() > 0, "Page Size must larger than zero");

    // hibernate的firstResult的序号从0开始
    criteria.setFirstResult(page.getFirst() - 1);
    criteria.setMaxResults(page.getPerpage());

    if (page.isOrderBySetted()) {
      String[] orderByArray = StringUtils.split(page.getOrderBy(), ',');
      String[] orderArray = StringUtils.split(page.getOrder(), ',');

      Assert.isTrue(orderByArray.length == orderArray.length, "分页多重排序参数中,排序字段与排序方向的个数不相等");

      for (int i = 0; i < orderByArray.length; i++) {
        if (Page.ASC.equals(orderArray[i])) {
          criteria.addOrder(Order.asc(orderByArray[i]));
        } else {
          criteria.addOrder(Order.desc(orderByArray[i]));
        }
      }
    }
    return criteria;
  }

  /**
   * 执行count查询获得本次Criteria查询所能获得的对象总数.
   */
  @SuppressWarnings("unchecked")
  protected long countCriteriaResult(final Criteria con) {
    CriteriaImpl impl = (CriteriaImpl) con;

    // 先把Projection、ResultTransformer、OrderBy取出来,清空三者后再执行Count操作
    Projection projection = impl.getProjection();

    List<CriteriaImpl.OrderEntry> orderEntries = null;
    try {
      orderEntries = (List) ReflectionUtils.getFieldValue(impl, "orderEntries");
      ReflectionUtils.setFieldValue(impl, "orderEntries", new ArrayList());
    } catch (Exception ex) {
      logger.error("不可能抛出的异常:{}", ex.getMessage());
      ex.printStackTrace();
    }

    // 执行Count查询
    Projection projection2 = Projections.rowCount();
    Criteria criteria = con.setProjection(projection2);
    Integer totalCountObject = null;
    try {
      totalCountObject = ((Long) criteria.uniqueResult()).intValue();
    } catch (Exception ex) {
      ex.printStackTrace();
    }

    // 将之前的Projection,ResultTransformer和OrderBy条件重新设回去
    con.setProjection(projection);

    ResultTransformer transformer = impl.getResultTransformer();
    if (projection == null) {
      con.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
    }
    if (transformer != null) {
      con.setResultTransformer(transformer);
    }
    try {
      ReflectionUtils.setFieldValue(impl, "orderEntries", orderEntries);
    } catch (Exception ex) {
      logger.error("不可能抛出的异常:{}", ex.getMessage());
      ex.printStackTrace();
    }
    
    long totalCount = (totalCountObject != null) ? totalCountObject : 0;
    return totalCount;
  }

  public List<T> findByPagination(final int firstResult, final int maxResult,
      final Criterion... criterions) {
    return findByPagination(entityClass, firstResult, maxResult, criterions);
  }

  /**
   * 分页查询 通过设置Criterion查询 注:不能满足要求子类重写该方法.
   * 
   * @param firstResult 开始
   * @param maxResult 最多多少
   * @param criterions 查询条件
   * @return 查询结果列表
   */
  @SuppressWarnings("unchecked")
  public List<T> findByPagination(final Class clazz, final int firstResult, final int maxResult,
      final Criterion... criterions) {
    Criteria criteria = getSession().createCriteria(clazz);
    for (Criterion c : criterions) {
      criteria.add(c);
    }
    criteria.setFirstResult(firstResult);
    criteria.setMaxResults(maxResult);
    return criteria.list();
  }
  
  /**
   * 分页查询 通过设置样例Example查询 注:不能满足要求子类重写该方法.
   * 
   * @param cls 实体类
   * @param firstResult 开始
   * @param maxResult 查询结果数
   * @return 返回分页查询的数据
   */
  @SuppressWarnings("unchecked")
  public List<T> findByPagination(final T cls, final int firstResult, final int maxResult) {
    Criteria criteria = getSession().createCriteria(entityClass);
    criteria.add(Example.create(cls).enableLike(MatchMode.ANYWHERE));
    criteria.setFirstResult(firstResult);
    criteria.setMaxResults(maxResult);
    return criteria.list();
  }

  /**
   * 分页统计查询 通过设置Criterion查询.
   * 
   * @return 一共有多少条数据
   */
  public int countByPagination(final Criterion... criterions) {
    return countByPagination(entityClass, criterions);
  }

  /**
   * 分页统计查询 通过设置Criterion查询.
   * 
   * @return 一共有多少条数据
   */
  public int countByPagination(final Class<T> clazz, final Criterion... criterions) {
    Criteria criteria = getSession().createCriteria(clazz);
    criteria.setProjection(Projections.rowCount());
    for (Criterion c : criterions) {
      criteria.add(c);
    }
    return (Integer) criteria.uniqueResult();
  }

  /**
   * 分页统计查询 通过设置样例Example查询.
   * 
   * @param cls 实体类
   * @return 一共有多少条数据
   */
  @SuppressWarnings("unchecked")
  public int countByPagination(final T cls) {
    Criteria criteria = getSession().createCriteria(entityClass);
    criteria.setProjection(Projections.rowCount());
    criteria.add(Example.create(cls).enableLike(MatchMode.ANYWHERE));
    return ((Long) criteria.uniqueResult()).intValue();

  }

  /**
   * 分页查询 得到结果集.
   * 
   * @param hql 符合HQL语法的查询语句
   * @param values 数量可变的条件值,按顺序绑定
   */
  public List<T> findPageList(String hql, final Object... values) {
    List<T> result = null;
    if (hql.toUpperCase().indexOf("FROM") != -1) {
      Page<T> page = (Page<T>) ThreadLocalContext.get("page");
      String newHql = "select count(*) " + hql.substring(hql.toUpperCase().indexOf("FROM"));
      Query newQuery = createQuery(newHql, values);
      setPageCount(newQuery, page);
      Query query = createQuery(hql, values);
      result = query.setFirstResult(page.getStart()).setMaxResults(page.getMax()).list();
    }
    return result;
  }

  /**
   * 给page设置count.
   */
  private void setPageCount(Query query, Page<T> page) {
    Number num = (Number) query.uniqueResult();
    if (num.intValue() > 0) {
      page.setCount(num.intValue());
    } else {
      page.setCount(0);
    }
  }

  /**
   * 分页查询 得到结果集.
   * 
   * @param sql 符合sql语法的查询语句
   * @param values 数量可变的条件值,按顺序绑定
   */
  public List<Object[]> findPageListBySql(String sql, final Object... values) {
    List<Object[]> result = null;
    if (sql.toUpperCase().indexOf("FROM") != -1) {
      Page<T> page = (Page<T>) ThreadLocalContext.get("page");
      String newSql = "select count(*) " + sql.substring(sql.toUpperCase().indexOf("FROM"));
      Query newQuery = createSqlQuery(newSql, values);
      setPageCount(newQuery, page);
      Query query = createSqlQuery(sql, values);
      result = query.setFirstResult(page.getStart()).setMaxResults(page.getMax()).list();
    }
    return result;
  }

  /**
   * 分页查询 得到结果集.
   * 
   * @param clazz 转化的实体 ---当实体不是持久化实体时，将查询结果依别名注入到clazz实体中。
   * @param isTransient 是否是持久化的实体 true不是持久化实体
   * @param sql 符合sql语法的查询语句
   * @param values 数量可变的条件值,按顺序绑定
   */
  public List<Object> findPageListBySql(Class<T> clazz, boolean isTransient, String sql,
      final Object... values) {
    List<Object> result = null;
    if (sql.toUpperCase().indexOf("FROM") != -1) {
      Page<T> page = (Page<T>) ThreadLocalContext.get("page");
      String newSql = "select count(*) " + sql.substring(sql.toUpperCase().indexOf("FROM"));
      Query newQuery = createSqlQuery(newSql, values);
      setPageCount(newQuery, page);
      SQLQuery query = createSqlQuery(sql, values);
      if (isTransient) {
        result = query.setResultTransformer(Transformers.aliasToBean(clazz))
            .setFirstResult(page.getStart()).setMaxResults(page.getMax()).list();
      } else {
        result = query.addEntity(clazz).setFirstResult(page.getStart()).setMaxResults(page.getMax())
            .list();
      }
    }
    return result;
  }

}
